1.窗口类型与层级
Android支持的窗口类型很多,不过我们可以将它们统一划分为三大类,即Application Window, System Window和Sub Window。另外各个种类下还细分为若干子类型,且都在WindowManager.java 中定义,如下所示。
1. Application Window
普通应用程序的窗口都属于这一类,如表1-1所示。
表 1-1 | Application Window 细分 |
Type | Description |
FIRST_APPLICATION_WINDOW= 1 | 应用程序窗口类型的起始值 |
TYPE_BASE_APPLICATION= 1 | 应用程序窗口类型的基础值,其他窗口类型以此为基础 |
TYPE_APPLICATION= 2 | 普通应用程序的窗口类型 |
TYPE_APPLICATION_STARTING= 3 | 应用程序的启动窗口类型。它不能由应用程序本身使用,而是Android系 统为应用程序启动前设计的窗口——当真正的应用窗口启动后就消失了 |
LAST_APPLICATION_WINDOW= 99 | 应用程序窗口类型的最大值 |
2. Sub Window
从“Sub Window”的字面意思可以了解到,这类窗口将附着在其他Window中,因而被称为 “子窗口”。具体包括如下子类型,如表1-2所示。
表 1-2 | Sub Window 细分 | |
Type | Description | |
FIRST_SUB_WINDOW= 1000 | 子窗口类型的起始值 | |
TYPE_APPLICATION_PANEL= FIRST_SUB_WINDOW | 应用程序的panel子窗口,在它的父窗口之上显示 | |
TYPE_APPLICATION_MED1A= FIRST_SUB_WINDOW+1 | 用于显示多媒体内容的子窗口,位于父窗口之下 TYPE_APPLICATION_MED1A=FIRST_SUB_WINDOW+1 | |
TYPE_APPLICATION_SUB_PANEL= FIRST_SUB_WINDOW+2 | 也是一种panel子窗口,位于父窗口以及所有TYPE_APPLICATION_ PANEL子窗口之上 TYPE_APPLICATION_SUB_PANEL=FIRST_SUB_WINDOW+2 | |
TYPE_APPLICAT1ON_ATTACHED_DIALOG= FIRST_SUB_WINDOW+3 | Dialog子窗口,如menu类型 TYPE_APPLICAT1ON_ATTACHED_DIALOG=FIRST_SUB_WINDOW+3 | |
TYPE_APPLICATION_MED1A_OVERLAY= FIRST_SUB_WINDOW+4 | 多媒体窗口的覆盖层,位于TYPE_APPLICATION_MEDIA和应用 程序窗口之间,通常需要是透明的才有意义。目前此类型处于未开 放状态 TYPE_APPLICATION_MED1A_OVERLAY=FIRST_SUB_WINDOW+4 | |
LAST_SUB_WINDOW= 1999 | 子窗口类型的结束值 LAST_SUB_WINDOW= 1999 |
3. System Window
系统程序所釆用的窗口类型,细分如表1-3所示。
Type | Description | |
FIRST_SYSTEM_WINDOW = 2000; | 系统窗口的起始值 | |
TYPE_STATUS_BAR = FI RST_SYSTEM_ WINDOW; | 系统状态栏窗口 | |
TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; | 搜索条窗口 | |
TYPE_PHONE=FIRST_SYSTEM_WINDOW+2; | 通话窗口,特别是来电通话。通常情况下它位 于系统状态栏之下,其他应用程序窗口之上 | |
TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; | Alert窗口,如电量不足的警告窗口。通常都 位于所有其他应用程序之上 | |
TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; | 屏保窗口 | |
TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; | 短暂的提示框窗口 | |
TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; | 系统覆盖层窗口,这种类型的窗口不能接收 input焦点,否则会与屏保发生冲突 | |
TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; | 电话优先窗口,如屏保状态下显示来电窗口 | |
TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; | 比如RecentAppsDialog就是这种类型的窗口 | |
TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WlNDOW+9; | 屏保时显示的对话框 | |
TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+lO; | 系统错误窗口 | |
TYPEJNPUT_METHOD = FIRST_SYSTEM_WINDOW+11; | 输入法窗口 | |
TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW+12; | 输入法窗口之上的对话框式窗口 | |
TYPE_WALLPAPER = FIRST_SYSTEM_WINDO W+13; | 壁纸窗口 | |
TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; | 滑动状态栏出现的窗口 | |
TYPE_SECURE_SYSTEM_OVERLAY=FIRST_SYSTEM1WINDOW+15; TYPE_DRAG= FIRST_S YSTEM_W(NDO W+16; TYPE_STATUS_BAR_SUB_PANEL= FIRST_SYSTEM_WINDOW+17; TYPE_POINTER= FIRST_S YSTEM_WINDO W+18; 3种类型 | 暂未开放 | |
TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19; | 导航条,可以参见本书应用篇的System UI 章节 | |
TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20; | 系统音量条 | |
TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21; | 启动时的进度条窗口 | |
TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22; | 当导航条隐藏时用于消耗触摸事件的伪窗口 | |
TYPE_DREAM= FIRST_SYSTEM_WlNDOW+23; TYPE NAVIGATlON BAR PANEL=FIRST SYSTEM WINDOW+24; | 暂未开放 | |
TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WlNDOW+25; | 在多用户系统中,将显示给所有用户 | |
TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26; | 用于模拟第二个显示设备 | |
TYPE_MAGNIFICAT1ON_OVERLAY = FIRST_SYSTEM_WINDOW+27; | 有点类似于“放大镜”的效果,用于放大显 示某部分内容 | |
TYPE_RECENTS_OVERLAY = FI RST_S YSTEM_ WINDO W+28; | 与 TYPE_SYSTEM_DIALOG 基本一致,区 別是 TYPE_RECENTS_OVERLAY 只显示 在一个用户的屏幕上 | |
LAST_SYSTEM_WINDOW = 2999; | 系统窗口结束 |
每种子类型后面都有一个数值,代表了它的窗口类型值。比如TYPE_STATUS_BAR= FIRST_ SYSTEM_WINDOW=2000。三大窗口类型的间隔是:
Application Window: 1-99
Sub Window: 1000-1999
System Window: 2000-2999
2.窗口属性(LayoutParams )
除了 WindowType外,WMS中还牵涉到很多其他的窗口属性。开发者可以通过设置不同的属性来使UI界面显示出各种样式——它们就像窗口用户与WMS间的协议一样,WMS就是为了实现 这些用户需求而努力的。另外,了解这些属性的含义也能帮助我们更好地理解WMS的内部原理。
这些属性统一放置在WindowManager.LayoutParams中,接下来我们分析其中几个重要的变量。
1.Type
也就是窗口类型,不再赘述。
2.Flags
窗口的标志,默认值是0。目前Android 4.3系统上支持的flag标志以及含义如表1.4所示。
表1-4 窗口标志释义 | |
Flags | Description |
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | 只要此窗口可见,即便屏幕处于开启状态也允许锁屏 |
FLAG_DIM_BEHFND | 在窗口后面的所有东西都将变暗淡(dimmed) |
FLAG_NOT_FOCUSABLE | 此窗口不获得输入焦点,意味着事件将发给该窗口后面的其他窗口。在 设置了此标志的同时,FLAG NOT TOUCH MODAL也会被同时设置 |
FLAG_NOT_TOUCHABLE | 和上面的标志类似,它表示该窗口不接受任何触摸事件 |
FLAG_NOT_TOUCH_MODAL | “无模式”的窗口。也就是说,在该窗口区域外的pointer事件将传给 它后面其他窗口,而不是由它自己来处理这些事件 |
FLAG_TOUCHABLE_WHEN_WAK1NG | 当设备进入睡眠状态时,设置此标志可以使你获得第一次的触摸事件 (因为这时候用户并不知道他们点击的是屏幕上的哪个位置,所以通 常这一事件是由系统自动处理的) |
FLAG_KEEP_SCREEN_ON | 只要这个窗口可见,屏幕就亮着 |
FLAG_LAYOUTJN_SCREEN | 窗口显示时不考虑系统装饰窗(比如Status Bar) |
FLAG LAYOUT NO LIMITS | 允许窗口超过屏幕区域 |
FLAG_FULLSCREEN | 隐藏所有的屏幕装饰窗口,切Status Bar。这对于视频播放器之类的应 用程序是很有用的 |
FLAG_FORCE_NOT_FULLSCREEN | 和上面的标志相反 |
FLAG_SECURE | 窗口内容被认为是保密的,因而它不会出现在截屏中,也不会在不安 全的屏幕上显示 |
FLAG_SCALED | 按照用户提供的参数做相应的伸缩调整 |
FLAG_IGNORE_CHEEK_PRESSES | 有的时候用户和屏幕会贴得很近,如打电话时。这种情况下岀现的某 些事件有可能是“无意”的,不应该响应 |
FLAG_LAYOUT_INSET_DECOR | 只能和FLAG_LAYOUT」N_SCREEN一起使用。当设置了 “全屏显示" 布局时,应用祖口部分&容而然可能会被系统装饰窗口覆盖。如果同时设 置了这一标志,系统将充分考虑装饰窗口所占的区域,以防止上述情况 |
FLAG_SHOW_WHEN_LOCKED | 使窗口能在锁屏窗口之上显示 |
FLAG_SHOW_WALLPAPER | 让壁纸在这个窗口之后显示。换句话说,当窗口是透明或半透明时就 可以看到后面的壁纸背景(如Launcher) |
FLAG_TURN_SCREEN_ON | 窗口显示时将屏幕点亮 |
FLAG_DISMISS_KEYGUARD | 设置这个标志可以解除屏幕锁,只要它不是一个secure lock |
FLAG_HARDWARE_ACCELERATED | 指明窗口是否需要硬件加速。当然,设置了这一标志并不能保证窗口 一定会得到硬件加速(取决于设备配置等一系列因素) |
可以通过以下方法来设置一个窗口标志:
Window w = activity.getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
3. systemUiVisibility
从名称可以看出,这个变量表示的是系统UI的可见性。
要特别注意的是,SystemUiVisibility的可选Flags值定义在View类中,而不是WindowManager, 具体如表1-5所示。
表 1-5 SystemUiVisibility 部分标志释义 | ||
Flags | Description | |
SYSTEM_U1_FLAG_VISIBLE | View视图请求显示System UI | |
SYSTEM_U1_FLAG_LOW_PROFILE | View 请求进入“low profile M 模式 意味着 Status Bar/Navigation icons有可能会被调暗。通常游戏、电子书阅读器等应用程序会有此模式需求 | |
SYSTEM _U1_FLAG_HIDE_NAVIGATION | 这个标志用于请求Navigation Bar的隐藏。这个标志经常与FLAG_LAYOUT_IN_SCREEN及 FLAG_LAYOUT_IN_SCREEN 一起使用,这样才能使应用程序的UI内容真正的全屏显示 | |
SYSTEM_UI_FLAG_FULLSCREEN | 整个 View将进入全屏显示。它和 WindowManager.LayoutParams. FLAG_ FULLSCREEN的视觉效果基本一致。不过和Window提供的标志不同的是,对于使用 Window.FEATURE_ACTION_BAR_OVERLAY 的 ActionBar, SystemUi中的这个标志同样能使它隐藏。 根据经验,如果是暂态的全屏显示需求,可以采用SYSTEM_UI_FLAG_ FULLSCREEN;而如果是长时间的全屏显示,如游戏开发而靠,就 最好使用Window标志 | |
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 参见本小节最后的范例。读者要注意区分它和SYSTEM_UI_FLAG_ FULLSCREEN 的差异 | |
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | 和上面标志的意义是类似的 | |
SYSTEM_U1_FLAG_LAYOUT_STABLE | 系统将尽量保持U1布局的稳定性 |
由前面的分析可知,我们只能对一个View (而不是Window)设置它的systemUiVisibility。 如果你希望某个Activity中的整棵View树都使用同一个systemUiVisibility,可以在Activity的 onCreate中加入类似于如下的代码段(在调用setContentView前):
int newVis = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getWindow().getDecorView().setSystemUiVisibility(newVis);
默认情况下,系统是从StatusBar下面的部分开始给应用程序做UI布局的。而上述这段代码 的目的就是使Activity以整个屏幕来布局——最为关键的是,同时还要显示StatusBar。显然 FLAG_FULLSCREEN不能符合这个需求(因为它会隐藏系统的装饰部分,包括StatusBar);而 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN这个标志会保留包括StatusBar在内的系统窗口, 同时会让View全屏显示。换句话说,你的View界面布局是从屏幕的(0,0)坐标开始的,但屏幕上方的一小部分内容会被StatusBar所遮盖。
那么,在什么场景下我们需要做这样的处理呢?
举个例子,现在很多定制手机的StatusBar是透明的。如果应用程序按照正常的做法从 StatusBar以下的位置开始布局,那么用户透过StatusBar看到的“画面”就是“黑的”:而如果从 (0,0)位置开始布局,那么用户就可以透过StatusBar看到应用程序的部分“View”内容了。另外, 有的应用程序需要频繁地在“显示系统装饰窗口”和“隐藏系统装饰窗口”间切换,为了让这两种情况下View本身的布局保持不变,也可以使用这一标志。